import {
	INodeExecutionData,
	type IExecuteFunctions,
	type INodeType,
	type INodeTypeDescription,
} from 'n8n-workflow';
import { dataCache, getCacheData, parseObject, taskCache } from './util';
import NodeCache from 'node-cache';

export class CacheNode implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'UNC缓存',
		name: 'uncTaskCache',
		icon: 'file:webhook.svg',
		group: ['transform'],
		version: 1,
		description: 'UNC任务缓存',
		defaults: {
			name: 'UNC Task Cache',
		},
		inputs: ['main'],
		outputs: ['main'],
		properties: [
			{
				displayName: '操作',
				name: 'action',
				type: 'options',
				default: 'GET',
				placeholder: '请选择一个操作',
				required: true,
				// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
				options: [
					{
						name: '获取',
						value: 'GET',
					},
					{
						name: '创建',
						value: 'SET',
					},
					{
						name: '哈希',
						value: 'ADD',
					},
					{
						name: '删除',
						value: 'DEL',
					},
					{
						name: '所有键',
						value: 'KEYS',
					},
					{
						name: '存在',
						value: 'EXISTS',
					}
				]
			},
			{
				displayName: '缓存Key',
				name: 'key',
				type: 'string',
				default: '',
				placeholder: '请输入缓存Key',
				required: true,
				displayOptions: {
					show: {
						action: [
							'ADD', 'SET', 'GET', 'DEL', 'EXISTS'
						],
					},
				},
			},
			{
				displayName: '缓存字段',
				name: 'field',
				type: 'string',
				default: '',
				placeholder: '请输入缓存字段, 为空则缓存所有输入参数(相当于HMSet)',
				displayOptions: {
					show: {
						action: [
							'ADD'
						],
					},
				},
			},
			{
				displayName: '缓存Value',
				name: 'value',
				type: 'string',
				required: true,
				displayOptions: {
					show: {
						action: [
							'SET', 'ADD'
						],
					},
				},
				default: '',
				placeholder: '请输入缓存Value',
			},
			{
				displayName: '缓存过期时间',
				name: 'ttl',
				type: 'number',
				required: true,
				displayOptions: {
					show: {
						action: [
							'SET', 'ADD'
						],
					},
				},
				default: 60,
				placeholder: '请输入缓存过期时间',
			},
			{
				displayName: '同步输出缓存对象',
				name: 'syncExecution',
				type: 'boolean',
				default: false,
			},
			{
				displayName: '缓存空间',
				name: 'namespace',
				type: 'options',
				default: 'dataCache',
				placeholder: '请选择一个缓存空间',
				required: true,
				// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
				options: [
					{
						name: '数据缓存',
						value: 'dataCache',
					},
					{
						name: '任务缓存',
						value: 'taskCache',
					}
				]
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items: any = [];
		let itemIndex = 0;
		const action = this.getNodeParameter('action', itemIndex) as string;
		let key = action !== 'KEYS' ? this.getNodeParameter('key', itemIndex) as string : '';
		const syncExecution = this.getNodeParameter('syncExecution', itemIndex) as boolean;
		const namespace = this.getNodeParameter('namespace', itemIndex) as string;
		const cacheObj: NodeCache = namespace === 'taskCache' ? taskCache : dataCache;
		try {
			if (action === 'SET' || action === 'ADD') {
				const value = this.getNodeParameter('value', itemIndex) as string;
				const ttl = Math.max(parseInt(this.getNodeParameter('ttl', 0) + ''), 0);
				if (action === 'ADD') {
					const field = this.getNodeParameter('field', itemIndex) as string;
					const lastData = getCacheData(key);
					const objValue = parseObject(value);
					const nowData = field ? { [field]: objValue } : objValue;
					if (typeof nowData === 'object') { // 只有是对象才能哈希
						const newValue = { ...lastData, ...nowData };
						// 写入缓存
						cacheObj.set(key, newValue, ttl);
					}
				} else {
					// 直接写入缓存
					cacheObj.set(key, value, ttl);
				}
				// 同步输出
				if (syncExecution) {
					items.push(...this.helpers.returnJsonArray(getCacheData(key)));
				} else {
					items.push(...this.helpers.returnJsonArray({ code: 0, action }));
				}
			} else if (action === 'GET') {
				items.push(...this.helpers.returnJsonArray(getCacheData(key)));
			} else if (action === 'DEL') {
				cacheObj.del(key);
				items.push(...this.helpers.returnJsonArray({ code: 0, action }));
			} else if (action === 'KEYS') {
				items.push(...this.helpers.returnJsonArray({ keys: cacheObj.keys() }));
			} else if (action === 'EXISTS') {
				items.push(...this.helpers.returnJsonArray({ exists: cacheObj.has(key) }));
			}
		} catch (error) {
			// This node should never fail but we want to showcase how
			if (this.continueOnFail()) {
				items.push({ json: {}, error, pairedItem: itemIndex });
			} else {
				if (error.context) {
					error.context.itemIndex = itemIndex;
				}
				throw error;
			}
		}
		return [items];
	}

}
